#pragma once

#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <functional>
#include <vector>
#include <unordered_map>
#include "Log.hpp"

static const std::string linesep = "\r\n";
static const std::string linesep1 = " ";
static const std::string linesep2 = ": ";
static const std::string linesep3 = ".";
static const std::string webroot = "./webroot";
static const std::string home = "index.html";
static const std::string html_404 = "404.html";
static const std::string argssep = "?";

class HttpRequest
{
private:
    std::string ReadOneLine(std::string &reqstr, bool *status)
    {
        auto pos = reqstr.find(linesep);
        if (pos == std::string::npos)
        {
            // 没找到
            *status = false;
            return std::string();
        }
        *status = true;
        auto line = reqstr.substr(0, pos);
        reqstr.erase(0, pos + linesep.size()); // 移除获取到的字符串
        return line;
    }

    void ParseReqLine(std::string &reqline)
    {
        std::stringstream ss(reqline);
        ss >> _method >> _uri >> _httpversion;
        // LOG(LogLevel::DEBUG) << "_method: " << _method;
        // LOG(LogLevel::DEBUG) << "_uri: " << _uri;
        // LOG(LogLevel::DEBUG) << "_httpversion: " << _httpversion;
    }

    void BulidKV(std::string &reqstr, std::string *key, std::string *val)
    {
        auto pos = reqstr.find(linesep2);
        if (pos == std::string::npos)
        {
            *key = *val = std::string();
            return;
        }
        *key = reqstr.substr(0, pos);
        *val = reqstr.substr(pos + linesep2.size());
    }

public:
    HttpRequest() {}
    // 发起请求才需要序列化, 服务端不需要序列化
    void Serialize() {}

    // 反序列化
    // 获取浏览器的请求, 将其反序列化
    bool Deserialize(std ::string &reqstr)
    {
        bool status = true;
        std::string reqline = ReadOneLine(reqstr, &status); // 读取请求行
        if (!status)
        {
            return false;
        }
        // std::cout << "#######################" << std::endl;
        // std::cout << reqline << std::endl;
        // std::cout << "#######################" << std::endl;
        // LOG(LogLevel::DEBUG) << reqline;
        // LOG(LogLevel::DEBUG) << "#############";

        // 细分请求行
        ParseReqLine(reqline);
        // LOG(LogLevel::DEBUG) << "_method: " << _method;
        // LOG(LogLevel::DEBUG) << "_uri: " << _uri;
        // LOG(LogLevel::DEBUG) << "_httpversion: " << _httpversion;

        while (true)
        {
            std::string key, val;
            std::string reqline = ReadOneLine(reqstr, &status); // 读取每一行
            if (status && !reqline.empty())
            {
                BulidKV(reqline, &key, &val);
                if (key.empty() || val.empty())
                {
                    continue;
                }
                _req_headers.insert(std::make_pair(key, val));
            }
            else if (status) // 读取到最后的空行
            {
                _blank_line = linesep1; // 报文读取完成
                break;
            }
            else
            {
                LOG(LogLevel::DEBUG) << "非法请求";
                break;
            }
        }
        // 正文
        _path = webroot;
        _path += _uri;
        if (_uri == "/")
        {
            _path += home;
        }

        // LOG(LogLevel::DEBUG) << "_path# " << _path;
        if (_method == "GET")
        {
            // ./webroot/login?username_name=yyc&password_name=555555
            auto pos = _path.find(argssep); // 找问号分隔符
            if (pos != std::string::npos)
            {
                // 截取问号前后字符串
                _req_body = _path.substr(pos + argssep.size()); // username_name=yyc&password_name=555555
                _path = _path.substr(0, pos);                   //./webroot/login
            }

            LOG(LogLevel::DEBUG) << "_path# " << _path;
            LOG(LogLevel::DEBUG) << "_req_body# " << _req_body;
        }
        else if (_method == "POST")
        {
            _req_body = reqstr;
        }

        return true;
    }

    std::string Path()
    {
        return _path;
    }

    std::string Body()
    {
        return _req_body;
    }

    void SetPath(const std::string &path)
    {
        _path = path;
    }

    std::string Suffix()
    {
        // _path: /login.html
        if (_path.empty())
        {
            return std::string();
        }
        else
        {
            auto pos = _path.rfind(linesep3); // 从最后面后往前找到第一个"."
            if (pos == std::string::npos)
            {
                return std::string();
            }
            else
            {
                return _path.substr(pos); // 截取文件类型
            }
        }
    }

    ~HttpRequest() {}

private:
    std::string _method;
    std::string _uri;
    std::string _httpversion;
    std::unordered_map<std::string, std::string> _req_headers;
    std::string _blank_line;
    std::string _req_body;
    std::vector<std::string> _cookie;
    std::string _path;
};

class HttpResponse
{
private:
    std::string CodeToDesc(int code)
    {
        switch (code)
        {
        case 200:
            /* code */
            return "OK";
        case 302:
            /* code */
            return "See Other";
        case 404:
            /* code */
            return "Not Found";
        case 301:
            /* code */
            return "Moved Permanently";
        default:
            return "";
        }
    }

public:
    HttpResponse()
        : _httpversion("HTTP/1.1"),
          _blank_line(linesep)
    {
    }

    // 序列化
    // 返回应答, 交给浏览器
    std::string Serialize()
    {
        std::string respstr = _httpversion + linesep1 + std::to_string(_code) + linesep1 + _desc + linesep;

        if (!_resp_body.empty())
        {
            std::string len = std::to_string(_resp_body.size());
            SetHeader("Content-Length", len);
        }

        for (auto &elem : _resp_headers)
        {
            std::string line = elem.first + linesep2 + elem.second + linesep;
            respstr += line;
        }

        for (auto &c : _cookie)
        {
            respstr += c;
            respstr += linesep;
        }

        respstr += _blank_line;
        respstr += _resp_body;

        return respstr;
    }

    // 服务端不需要做应答反序列化, 这是交给浏览器做的事
    void Deserialize() {}

    // 让ai形成的
    bool ReadContent(const std::string &path)
    {
        // 以二进制方式读取文件
        //  打开文件（二进制模式 + 输入模式）
        std::ifstream file(path, std::ios::binary | std::ios::in);
        if (!file.is_open())
        {
            LOG(LogLevel::DEBUG) << path << "资源不存在";
            return false;
        }

        // 移动到文件末尾获取文件大小
        file.seekg(0, std::ios::end);
        std::streamsize fileSize = file.tellg(); // 获取文件大小
        file.seekg(0, std::ios::beg);            // 回到文件开头

        // 预留string空间（避免多次内存分配）
        _resp_body.resize(static_cast<size_t>(fileSize));
        file.read(&_resp_body[0], fileSize);
        file.close();
        return true;
    }

    void SetCode(int code)
    {
        if (code >= 100 && code < 600)
        {
            _code = code;
            _desc = CodeToDesc(_code);
        }
        else
        {
            LOG(LogLevel::DEBUG) << "非法状态码: " << code;
        }
    }

    bool SetHeader(const std::string &key, const std::string &value)
    {
        _resp_headers[key] = value;
        return true;
    }

    void SetCookie(const std::string &key, const std::string &value)
    {
        std::string cookie = key;
        cookie += ": ";
        cookie += value;
        _cookie.push_back(cookie);
    }

    ~HttpResponse() {}

private:
    std::string _httpversion;
    int _code;
    std::string _desc;
    std::unordered_map<std::string, std::string> _resp_headers;
    std::vector<std::string> _cookie;
    std::string _blank_line;
    std::string _resp_body;
};

using func_t = std::function<HttpResponse(HttpRequest &)>;

class Http
{
private:
    std::string SuffixToDesc(std::string suffix)
    {
        if (suffix == ".html")
        {
            return "text/html";
        }
        else if (suffix == ".js")
        {
            return "application/javascript";
        }
        else if (suffix == ".css")
        {
            return "text/css";
        }
        else if (suffix == ".png")
        {
            return "image/png";
        }
        else if (suffix == ".jpg")
        {
            return "image/jpeg";
        }
        else
        {
            return std::string();
        }
    }

public:
    Http() {}

    void Register(const std::string &action, func_t handler)
    {
        std::string key = webroot;
        key += action;
        _handlers[key] = handler;
    }

    std::string Hanlerrequst(std::string &reqstr)
    {
        std::string respstr;
        HttpRequest req;
        if (req.Deserialize(reqstr))
        {
            // 交互式处理
            HttpResponse resp;
            std::string target = req.Path();
            if (_handlers.find(target) != _handlers.end())
            {
                resp = _handlers[target](req);
            }
            else
            {
                // test 重定向
                // if(req.Path() == "./webroot/redir")
                // {
                //     resp.SetCode(302);//临时重定向
                //     resp.SetHeader("Location", "https://www.baidu.com/");

                // }
                // 读取目标服务器中指定路径下文件内容

                // 处理静态网页资源
                if (resp.ReadContent(req.Path()))
                {
                    // 提取资源文件后缀
                    std::string suffix = req.Suffix();
                    // 获取文件类型
                    std::string mime_type_value = SuffixToDesc(suffix);
                    resp.SetHeader("Connect-Type", mime_type_value);
                    resp.SetCode(200);
                }
                else
                {
                    // 资源不存在
                    std::string err_404 = webroot + "/" + html_404;
                    req.SetPath(err_404);
                    resp.ReadContent(req.Path());

                    // 用重定向的方法来实现404
                    resp.SetCode(301);
                    resp.SetHeader("Location", "/404.html");

                    // // 获取文件类型
                    // std::string suffix = req.Suffix();
                    // std::string mime_type_value = suffixToDesc(suffix);
                    // resp.SetHeader("Connect-Type", mime_type_value);
                    // resp.SetCode(404);
                }
            }

            respstr = resp.Serialize();
        }

        return respstr;
    }
    ~Http() {}

private:
    // http的功能路由功能
    std::unordered_map<std::string, func_t> _handlers;
};